package com.xu.ai.model.dashscope.config;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.web.client.RestClientCustomizer;
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.StreamUtils;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;

import reactor.core.publisher.Mono;

/**
 * 配置RestClient和WebClient
 *
 * @author xuguan
 * @since 2025/10/24
 * @see org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration
 * @see org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration
 */
@Configuration
public class RestConfig {

	private static final Logger log = LoggerFactory.getLogger("org.springframework.web");

	@Bean
	public RestClientCustomizer restClientCustomizer() {
		return restClientBuilder -> restClientBuilder
			.requestFactory(new BufferingClientHttpRequestFactory(ClientHttpRequestFactoryBuilder.detect().build()))
			.requestInterceptor(new LoggingClientHttpRequestInterceptor());
	}

	@Bean
	public WebClientCustomizer webClientCustomizer() {
		return webClientBuilder -> webClientBuilder
			.filter(logClientRequest())
			.filter(logClientResponse());
	}

	private static class LoggingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

		@Override
		public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
				throws IOException {
			logHttpRequest(request, body);
			ClientHttpResponse response = null;
			try {
				response = execution.execute(request, body);
				return response;
			}
			finally {
				if (Objects.isNull(response)) {
					log.error("响应为空");
				}
				else {
					logHttpResponse(response);
				}
			}
		}

		private void logHttpRequest(HttpRequest request, byte[] body) {
			if (log.isDebugEnabled()) {
				StringBuilder msg = new StringBuilder();
				msg.append("Request [")
					.append(request.getMethod())
					.append(" ")
					.append(request.getURI())
					.append(", headers=")
					.append(request.getHeaders())
					.append(", payload=")
					.append(new String(body, StandardCharsets.UTF_8))
					.append("]");
				log.info(msg.toString());
			}
		}

		private void logHttpResponse(ClientHttpResponse response) throws IOException {
			if (log.isDebugEnabled()) {
				StringBuilder msg = new StringBuilder();
				msg.append("After request [")
					.append(", statusCode=")
					.append(response.getStatusCode())
					.append(", statusText=")
					.append(response.getStatusText())
					.append(", payload=")
					.append(StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8))
					.append("]");
				log.info(msg.toString());
			}
		}

	}

	private ExchangeFilterFunction logClientRequest() {
		return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
			if (log.isDebugEnabled()) {
				log.debug("Request: {} {}", clientRequest.method(), clientRequest.url());

				clientRequest.headers()
					.forEach(
							(name, values) -> values.forEach(value -> log.debug("Request Header: {}={}", name, value)));
			}
			return Mono.just(clientRequest);
		});
	}

	private ExchangeFilterFunction logClientResponse() {
		return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
			if (log.isDebugEnabled()) {
				log.debug("Response Status: {}", clientResponse.statusCode());

				clientResponse.headers()
					.asHttpHeaders()
					.forEach((name, values) -> values
						.forEach(value -> log.debug("Response Header: {}={}", name, value)));
			}
			return Mono.just(clientResponse);
		});
	}

}
